// SPDX-License-Identifier: GPL-2.0
/*
 * proc_antos.c -- provide a proc access method for antos
 *
 * Copyright (C) 2020 HUAWEI, Inc.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/file.h>
#include <linux/fs.h>
#include <linux/kallsyms.h>
#include <linux/pid_namespace.h>
#include <linux/proc_fs.h>
#include <linux/ptrace.h>
#include <linux/sched/debug.h>
#include <linux/sched/stat.h>
#include <linux/seq_file.h>
#include <linux/stat.h>

#include "internal.h"

static void get_printable_cmdline(struct seq_file *m, struct task_struct *task)
{
	int i, ret;

	ret = get_cmdline(task, m->buf + m->count, m->size - m->count);
	if (ret > 0) {
		for (i = 0; i < ret; i++) {
			if (*(m->buf + m->count + i) == '\0')
				*(m->buf + m->count + i) = ' ';
		}
		m->count += ret;
	}
	seq_putc(m, '\n');
}

static void get_task_schedstat(struct seq_file *m, struct task_struct *task)
{
	if (unlikely(!sched_info_on())) {
		seq_puts(m, "0 0 0\n");
		return;
	}

	seq_printf(m, "%llu %llu %lu\n",
			(unsigned long long)task->se.sum_exec_runtime,
			(unsigned long long)task->sched_info.run_delay,
			task->sched_info.pcount);
}

static void get_task_wchan(struct seq_file *m, struct task_struct *task)
{
	unsigned long wchan;
	char symname[KSYM_NAME_LEN];

	if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
		wchan = get_wchan(task);
		if (wchan && !lookup_symbol_name(wchan, symname)) {
			seq_puts(m, symname);
			seq_putc(m, '\n');
			return;
		}
	}

	seq_putc(m, '0');
	seq_putc(m, '\n');
}

void proc_antos_task_info_query(struct seq_file *m, struct pid_namespace *ns,
		struct pid *pid, struct task_struct *p, enum proc_task_query_type type)
{
	if (m == NULL || p == NULL)
		return;

	switch (type) {
	case TASK_QUERY_TGID_STAT:
		if (ns && pid)
			proc_tgid_stat(m, ns, pid, p);
		break;
	case TASK_QUERY_TID_STAT:
		if (ns && pid)
			proc_tid_stat(m, ns, pid, p);
		break;
	case TASK_QUERY_PID_STATUS:
		if (ns && pid)
			proc_pid_status(m, ns, pid, p);
		break;
	case TASK_QUERY_PID_STATM:
		if (ns && pid)
			proc_pid_statm(m, ns, pid, p);
		break;
	case TASK_QUERY_PID_SCHEDSTAT:
		get_task_schedstat(m, p);
		break;
	case TASK_QUERY_WCHAN:
		get_task_wchan(m, p);
		break;
	case TASK_QUERY_CMDLINE:
		get_printable_cmdline(m, p);
		break;
	case TASK_QUERY_SCHED:
		if (ns)
			proc_sched_show_task(p, ns, m);
		break;
	default:
		break;
	}
}
EXPORT_SYMBOL(proc_antos_task_info_query);
